2.9.3. The void * Type and Type Recasting

C 类型void *表示通用指针——指向任何类型的指针,或指向未指定类型的指针。 C 允许通用指针类型,因为系统上的内存地址始终存储在相同数量的字节中(例如,地址在 32 位系统上为 4 个字节,在 64 位系统上为 8 个字节)。因此,每个指针变量都需要相同数量的存储字节,并且由于它们的大小相同,因此编译器可以在 void *不知道变量指向的类型的情况下为变量分配空间。这是一个例子:

void *gen_ptr;
int x;
char ch;

gen_ptr = &x;  // gen_ptr can be assigned the address of an int
gen_ptr = &ch; // or the address of a char (or the address of any type)

通常,程序员不会void *像前面的示例那样声明类型变量。相反,它通常用于指定函数的通用返回类型或函数的通用参数。该void *类型通常被函数用作返回类型,这些函数返回新分配的内存,该内存可用于存储任何类型(例如,malloc)。它还用作可以采用任何类型值的函数的函数参数。在这种情况下,对函数的单独调用会传入指向某种特定类型的指针,该指针可以传递给函数的void *参数,因为它可以存储任何类型的地址。

因为void *是通用指针类型,所以不能直接取消引用——编译器不知道地址指向的内存大小。例如,地址可以指代int四个字节的存储位置,或者可以指char代存储器中的一个字节的存储位置。因此,程序员必须在取消引用之前显式地 将指针重新转换void *为特定类型的指针。重铸告诉编译器指针变量的具体类型,允许编译器为指针解引用生成正确的内存访问代码。

下面是两个void *使用示例:

  1. 调用将mallocvoid *返回类型重新转换为用于存储返回的堆内存地址的变量的特定指针类型:

    int *array;
    char *str;
    
    array = (int *)malloc(sizeof(int) * 10); // recast void * return value
    str = (char *)malloc(sizeof(char) * 20);
    
    *array = 10;
    str[0] = 'a';
    
  2. 创建线程的时候经常会遇到 void *。在线程函数中使用 void * 参数类型允许线程采用任何类型的应用程序特定指针。 pthread_create 函数有一个用于线程主函数的参数和一个用于传递给新创建的线程将执行的线程主函数的参数值的 void * 参数。 void *参数的使用使得 pthread_create 成为一个通用的线程创建函数;它可用于指向任何类型的内存位置。对于调用 pthread_create 的特定程序,程序员知道传递给 void * 参数的实参类型,因此程序员必须在取消引用它之前将其重新转换为其已知类型。在此示例中,假设传递给 args 参数的地址包含整型变量的地址:

    /*
     * an application-specific pthread main function
     * must have this function prototype: int func_name(void *args)
     *
     * any given implementation knows what type is really passed in
     *  args: pointer to an int value
     */
    int my_thr_main(void *args) {
    	int num;
    
    	// first recast args to an int *, then dereference to get int value
    	num = *((int *)args);  // num gets 6
    	...
    }
    
    int main(void) {
    	int ret, x;
    	pthread_t tid;
    
    	x = 6;
    	// pass the address of int variable (x) to pthread_create's void * param
    	// (we recast &x as a (void *) to match the type of pthread_create's param)
    	ret = pthread_create(&tid, NULL,
    						 my_thr_main,    // a thread main function
    						 (void *)(&x));  // &x will be passed to my_thr_main
    	// ...